1"use client";
2
3
20import { useParams } from "next/navigation";
21import { ShieldCheck, Info } from "lucide-react";
22import { useWorkspace } from "@/hooks/use-workspace";
23import { SettingsShell, Section } from "@/components/settings/settings-shell";
24
25const LEVELS = ["NONE", "VIEW", "COMMENT", "EDIT", "FULL_ACCESS"] as const;
26
27const LEVEL_DESC: Record<string, string> = {
28 NONE: "No access.",
29 VIEW: "Can read content.",
30 COMMENT: "Can view and comment.",
31 EDIT: "Can view, comment, and edit content.",
32 FULL_ACCESS: "Can view, comment, edit, share, and manage settings."
33};
34
35const ROLE_LEVEL: { role: string; level: string }[] = [
36 { role: "OWNER", level: "FULL_ACCESS" },
37 { role: "ADMIN", level: "FULL_ACCESS" },
38 { role: "MEMBER", level: "EDIT" },
39 { role: "GUEST", level: "VIEW" }
40];
41
42export default function SettingsPermissionsPage() {
43 const params = useParams<{ workspace: string }>();
44 const slugOrId = params?.workspace ?? "";
45 const { data: ws } = useWorkspace(slugOrId);
46
47 const currentRole = (ws?.role ?? "").toUpperCase();
48
49 return (
50 <SettingsShell
51 title="Permissions"
52 description="How access is resolved in this workspace."
53 active="permissions"
54 >
55 <div className="space-y-5">
56 <Section title="Access levels">
57 <ul className="space-y-1">
58 {LEVELS.map((lvl) => (
59 <li key={lvl} className="flex items-start gap-2 text-sm">
60 <span className="w-24 shrink-0 rounded bg-muted px-1.5 py-0.5 text-center text-[11px] font-medium text-muted-foreground">
61 {lvl}
62 </span>
63 <span className="text-muted-foreground">{LEVEL_DESC[lvl]}</span>
64 </li>
65 ))}
66 </ul>
67 </Section>
68
69 <Section title="Role → baseline access">
70 <div className="overflow-x-auto">
71 <table className="w-full text-sm">
72 <thead>
73 <tr className="text-left text-xs text-muted-foreground">
74 <th className="py-1 pr-4 font-medium">Role</th>
75 <th className="py-1 pr-4 font-medium">Baseline level</th>
76 </tr>
77 </thead>
78 <tbody>
79 {ROLE_LEVEL.map((r) => (
80 <tr key={r.role} className="border-t border-border">
81 <td className="py-1.5 pr-4">
82 <span className="inline-flex items-center gap-1.5">
83 <span className="capitalize">{r.role}</span>
84 {currentRole === r.role ? (
85 <span className="rounded-full bg-primary/15 px-1.5 py-0.5 text-[10px] text-primary">you</span>
86 ) : null}
87 </span>
88 </td>
89 <td className="py-1.5 pr-4">
90 <span className="rounded bg-muted px-1.5 py-0.5 text-[11px] font-medium text-muted-foreground">{r.level}</span>
91 </td>
92 </tr>
93 ))}
94 </tbody>
95 </table>
96 </div>
97 <p className="mt-2 text-xs text-muted-foreground">
98 Your effective level on any resource is the <em>maximum</em> of your role's baseline level and the most
99 specific per-resource grant that applies.
100 </p>
101 </Section>
102
103 <Section title="Per-resource permissions">
104 <div className="flex items-start gap-2 rounded-md border border-dashed border-border p-3 text-sm text-muted-foreground">
105 <Info className="mt-0.5 size-4 shrink-0" aria-hidden />
106 <div>
107 <p>
108 Fine-grained grants (per page, database, teamspace, or record, for a specific user or team) are
109 enforced server-side. A management surface for these grants isn't exposed in the API yet, so they
110 can't be edited here.
111 </p>
112 <p className="mt-1 flex items-center gap-1.5">
113 <ShieldCheck className="size-3.5" aria-hidden /> All checks are authoritative on the server; the client
114 never trusts its own role.
115 </p>
116 </div>
117 </div>
118 </Section>
119 </div>
120 </SettingsShell>
121 );
122}
123